package com.laity.readWriteLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.readWriteLock.ReadWriteLockDemo
 * @Date: 2023年11月26日 23:28
 * @Description: ReadWriteLock 读写锁
 */
/*
读写锁的特点：
 读 - 读 可以共存并发
 写 - 写 不能共存并发
 读 - 写 不能共存并发
独占锁：一次只能被一个线程占用
共享锁：同时可以被多个线程占用
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
//         Cache cache = new Cache();
        CacheLock cache = new CacheLock();

        for (int i = 1; i < 5; i++) {
            final int index = i;
            new Thread(() -> {
                cache.put("key", "value" + index);
            }, "写线程" + i).start();
            new Thread(() -> {
                System.out.println(cache.get("key"));
            }, "读线程" + i).start();
        }
    }
}

// 自定义未加锁的一个缓存
class Cache {
    private final Map<String, Object> cache = new HashMap<>();

    // 读方法
    public Object get(String key) {
        System.out.println(Thread.currentThread().getName() + " 正在读取数据");
        Object o = cache.get(key);
        System.out.println(Thread.currentThread().getName() + " 读取数据完毕");
        return o;
    }

    // 写方法
    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() + " 正在写入数据");
        cache.put(key, value);
        System.out.println(Thread.currentThread().getName() + " 写入数据完毕");
    }
}

class CacheLock {
    private final Map<String, Object> cache = new HashMap<>();

    // 读写锁 读写锁可以保证读写操作的顺序 更加细粒度控制
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 这是一个正常的可重入锁 - 普通锁
    // private final Lock lock  = new ReentrantLock();

    // 读方法，读取的时候，可以允许多个线程同时读取
    public Object get(String key) {
        readWriteLock.readLock().lock();
        Object o = null;
        try {
            System.out.println(Thread.currentThread().getName() + " 正在读取数据");
            o = cache.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
        System.out.println(Thread.currentThread().getName() + " 读取数据完毕");
        return o;
    }

    // 写方法，写入的时候，只希望同时只有一个线程可以写入
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在写入数据");
            cache.put(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
        System.out.println(Thread.currentThread().getName() + " 写入数据完毕");
    }
}
